/* $Id: AdminClientCompanyReviewFacadeImpl.java 
 * Copyright (C) 2011, TRAKHEES
 *  
 * Revision History
 *
 * Date  			Author 		    	Remarks
 * ---------------------------------------------------------------------------------
 *
 */
package ae.pcfc.etks.online.common.facade.provider;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.springframework.transaction.annotation.Transactional;

import ae.pcfc.etks.online.common.ConstantsCommon;
import ae.pcfc.etks.online.common.ConstantsCommon.WorkflowDepartment;
import ae.pcfc.etks.online.common.domain.AppMetaData;
import ae.pcfc.etks.online.common.domain.Client;
import ae.pcfc.etks.online.common.domain.ClientServiceRole;
import ae.pcfc.etks.online.common.domain.ProcStepActions;
import ae.pcfc.etks.online.common.domain.ProcStepsComnts;
import ae.pcfc.etks.online.common.domain.ProcessStep;
import ae.pcfc.etks.online.common.domain.Service;
import ae.pcfc.etks.online.common.domain.ServiceRole;
import ae.pcfc.etks.online.common.domain.TTscClientRoleToDepartmentMap;
import ae.pcfc.etks.online.common.domain.TTscDepartments;
import ae.pcfc.etks.online.common.domain.User;
import ae.pcfc.etks.online.common.domain.UserReq;
import ae.pcfc.etks.online.common.domain.UserServiceSecurity;
import ae.pcfc.etks.online.common.domain.manager.AppMetaDataDomainObjectManager;
import ae.pcfc.etks.online.common.domain.manager.ClientDomainObjectManager;
import ae.pcfc.etks.online.common.domain.manager.ProcessDomainObjectManager;
import ae.pcfc.etks.online.common.domain.manager.ProcessStepActionDomainObjectManager;
import ae.pcfc.etks.online.common.domain.manager.ServiceDomainObjectManager;
import ae.pcfc.etks.online.common.domain.manager.SingleClientCompanyDomainObjectManager;
import ae.pcfc.etks.online.common.domain.manager.UserDomainObjectManager;
import ae.pcfc.etks.online.common.domain.manager.UserReqDomainObjectManager;
import ae.pcfc.etks.online.common.domain.manager.UserServiceSecurityDomainObjectManager;
import ae.pcfc.etks.online.common.domain.manager.UtilityDomainObjectManager;
import ae.pcfc.etks.online.common.exception.InvalidInputException;
import ae.pcfc.etks.online.common.facade.AdminClientCompanyReviewFacade;
import ae.pcfc.etks.online.common.vo.AdminApprovalVO;
import ae.pcfc.etks.online.common.vo.ClientCompanyVO;
import ae.pcfc.etks.online.common.vo.SCClientCompanyRoleVO;
import ae.pcfc.etks.online.wf.service.client.ClientAdminWF;
import ae.pcfc.etks.online.wf.service.client.vo.ClientAdminCompleteStepVO;
import ae.pcfc.etks.online.wf.service.client.vo.CompleteStepVO;

/**
 * @author Dicetek.Ismail
 *
 */
public class AdminClientCompanyReviewFacadeImpl extends BaseFacade implements AdminClientCompanyReviewFacade {
    	/*
	 * Here we coordinate between the all interacting business domain objects
	 * (BO), transactions are maintained here. use case business validation are
	 * maintained here (that requires DB checks or retrieving data from DB)
	 */
	private ClientDomainObjectManager clientDomainObjectManager;
	private ProcessDomainObjectManager processDomainObjectManager;
	private AppMetaDataDomainObjectManager appMetaDataDomainObjectManager;
	private SingleClientCompanyDomainObjectManager singleClientCompanyDomainObjectManager;
	private ServiceDomainObjectManager serviceDomainObjectManager;
	private UserServiceSecurityDomainObjectManager userServiceSecurityDomainObjectManager;
	private ClientAdminWF clientCompanyAdminWF;
	private UtilityDomainObjectManager utilityDomainObjectManager;
	private ProcessStepActionDomainObjectManager processStepActionDomainObjectManager;
	private UserReqDomainObjectManager userReqDomainObjectManager;
	private UserDomainObjectManager userDomainObjectManager;
	

	public UserDomainObjectManager getUserDomainObjectManager() {
	    return userDomainObjectManager;
	}

	public void setUserDomainObjectManager(
		UserDomainObjectManager userDomainObjectManager) {
	    this.userDomainObjectManager = userDomainObjectManager;
	}

	public UserReqDomainObjectManager getUserReqDomainObjectManager() {
	    return userReqDomainObjectManager;
	}

	public void setUserReqDomainObjectManager(
		UserReqDomainObjectManager userReqDomainObjectManager) {
	    this.userReqDomainObjectManager = userReqDomainObjectManager;
	}

	public UtilityDomainObjectManager getUtilityDomainObjectManager() {
		return utilityDomainObjectManager;
	}

	public void setUtilityDomainObjectManager(
			UtilityDomainObjectManager utilityDomainObjectManager) {
		this.utilityDomainObjectManager = utilityDomainObjectManager;
	}

	public ProcessStepActionDomainObjectManager getProcessStepActionDomainObjectManager() {
		return processStepActionDomainObjectManager;
	}

	public void setProcessStepActionDomainObjectManager(
			ProcessStepActionDomainObjectManager processStepActionDomainObjectManager) {
		this.processStepActionDomainObjectManager = processStepActionDomainObjectManager;
	}

	public void setClientDomainObjectManager(ClientDomainObjectManager clientDomainObjectManager) {
		this.clientDomainObjectManager = clientDomainObjectManager;
	}

	public void setProcessDomainObjectManager(ProcessDomainObjectManager processDomainObjectManager) {
		this.processDomainObjectManager = processDomainObjectManager;
	}

	public void setAppMetaDataDomainObjectManager(AppMetaDataDomainObjectManager appMetaDataDomainObjectManager) {
		this.appMetaDataDomainObjectManager = appMetaDataDomainObjectManager;
	}

	public void setSingleClientCompanyDomainObjectManager(
			SingleClientCompanyDomainObjectManager singleClientCompanyDomainObjectManager) {
		this.singleClientCompanyDomainObjectManager = singleClientCompanyDomainObjectManager;
	}

	public void setServiceDomainObjectManager(ServiceDomainObjectManager serviceDomainObjectManager) {
		this.serviceDomainObjectManager = serviceDomainObjectManager;
	}

	public void setUserServiceSecurityDomainObjectManager(
			UserServiceSecurityDomainObjectManager userServiceSecurityDomainObjectManager) {
		this.userServiceSecurityDomainObjectManager = userServiceSecurityDomainObjectManager;
	}

	public void setClientCompanyAdminWF(ClientAdminWF clientCompanyAdminWF) {
		this.clientCompanyAdminWF = clientCompanyAdminWF;
	}

	/**
	 * getClientCompanyByApplicationId
	 * 
	 * @see ae.pcfc.etks.online.common.facade.AdminClientCompanyReviewFacade#getClientCompanyByApplicationId(java.lang.Long)
	 * 
	 */
	@Transactional(readOnly = true)
	public ClientCompanyVO getClientCompanyByApplicationId(Long applicationId) {

		/*
		 * ClientReq clientReq =
		 * companyDomainObjectManager.getClientByApplicationId(applicationId);
		 * 
		 * if(clientReq == null ) return null;
		 * 
		 * ClientCompanyVO vo = new ClientCompanyVO();
		 * 
		 * getMapper().map(clientReq, vo);
		 * 
		 * Iterator<UserReq> iterator = clientReq.getUserReqs().iterator();
		 * UserReq userReq;
		 * 
		 * while(iterator.hasNext()) { userReq = iterator.next();
		 * 
		 * vo.setStrCompNameArab(userReq.getUReqNameArb());
		 * vo.setStrCompNameEng(userReq.getUReqNameEng());
		 * vo.setStrUserName(userReq.getUReqUsername());
		 * vo.setStrEmailAddress(userReq.getUReqEmail());
		 * vo.setStrMobileNumber(userReq.getUReqMobile());
		 * vo.setStrEmailNotification(userReq.getUReqEmailNotification());
		 * vo.setStrMobileNotification(userReq.getUReqSmsNotification());
		 * 
		 * Iterator<UserDocumentsReq> itrUserDocs =
		 * userReq.getUserDocumentsReqs().iterator();
		 * 
		 * UserDocumentsReq userDocumentsReq;
		 * 
		 * while(itrUserDocs.hasNext()) { userDocumentsReq = itrUserDocs.next();
		 * vo.setStrUploadDocument(userDocumentsReq.getUdReqUrl()); } } return
		 * vo;
		 */
		return null;

	}
	/**
	 * reviewRegisterClientCompany
	 * @return 
	 * 
	 * @see ae.pcfc.etks.online.common.facade.AdminClientCompanyReviewFacade#reviewRegisterClientCompany(ae.pcfc.etks.online.common.vo.AdminApprovalVO, ae.pcfc.etks.online.common.vo.ClientCompanyVO)
	 * 
	 */
	@Transactional
	public ClientAdminCompleteStepVO reviewRegisterClientCompany(AdminApprovalVO adminApprovalVO, ClientCompanyVO clientCompanyVO) {

	    	AppMetaData appMetaData = appMetaDataDomainObjectManager.findByPrimaryKeyProcessAndStepCode(adminApprovalVO
    			.getStrApplicationId().toString(), adminApprovalVO.getProcessCode().getProcessCode(), adminApprovalVO.getStrStepName());

        	if (appMetaData != null) {
        		/************ Storing Meta Data in Tables ***********************/
        	    	if(appMetaDataDomainObjectManager.validateAppMetaDataWithStep(appMetaData,adminApprovalVO.getStrStepName())){
		    	    throw new InvalidInputException();
		    	}
				String wfStatusEn = appMetaData.getWfStatusEn();
				String wfStatusAr = appMetaData.getWfStatusAr();
        		getMapper().map(adminApprovalVO, appMetaData);
				appMetaData.setWfStatusEn(wfStatusEn);
				appMetaData.setWfStatusAr(wfStatusAr);
        		this.appMetaDataDomainObjectManager.updateAppMetaDataWithActionTaken(appMetaData, adminApprovalVO.getStrStepName());
        		/************** End Store Meta Date ***************************/
        		ClientAdminCompleteStepVO completeStepVO = getCompleteStepVO(adminApprovalVO,clientCompanyVO, appMetaData);
        
        		if(clientCompanyAdminWF.getFinDeptStepName().equalsIgnoreCase(adminApprovalVO.getStrStepName()) && adminApprovalVO.getStrActionType().equals(ConstantsCommon.APPROVE)){
        		    	// If Approved by Finance department, send the approval message to the Ultimus alone and update the application request in the DB as approved
        			createClientCompanyAdmin(adminApprovalVO, clientCompanyVO);
        			UserReq userReq = this.userReqDomainObjectManager.findByApplicationId(adminApprovalVO.getStrApplicationId());
				userReq.setUReqStatus(ConstantsCommon.APPROVE);
				this.userReqDomainObjectManager.update(userReq);
			    	appMetaData.setWfStatus(ConstantsCommon.APPROVE);
					appMetaData.setWfStatusEn(adminApprovalVO.getStrStatusEn());
					appMetaData.setWfStatusAr(adminApprovalVO.getStrStatusAr());
					clientCompanyAdminWF.updateCompleteStepVO(completeStepVO, adminApprovalVO.getStrStepName(), ConstantsCommon.WORK_FLOW_STATUS_APPROVE, adminApprovalVO.getStrUserNotification());
        		}else if(adminApprovalVO.getStrActionType().equals(ConstantsCommon.APPROVE)){
        		    	// If Approved by any other departments, send the approval message to the Ultimus alone
        		    clientCompanyAdminWF.updateCompleteStepVO(completeStepVO, adminApprovalVO.getStrStepName(), ConstantsCommon.WORK_FLOW_STATUS_APPROVE, adminApprovalVO.getStrUserNotification());
        		}else if(adminApprovalVO.getStrActionType().equals(ConstantsCommon.REJECT)){
        		    // If Rejected, update the application request in DB as rejected and send the rejection message to the Ultimus
			    	UserReq userReq = this.userReqDomainObjectManager.findByApplicationId(adminApprovalVO.getStrApplicationId());
				userReq.setUReqStatus(ConstantsCommon.REJECT);
				this.userReqDomainObjectManager.update(userReq);
        		    appMetaData.setWfStatus(ConstantsCommon.REJECT);
        			appMetaData.setWfStatusEn(adminApprovalVO.getStrStatusEn());
        			appMetaData.setWfStatusAr(adminApprovalVO.getStrStatusAr());
        			clientCompanyAdminWF.updateCompleteStepVO(completeStepVO, adminApprovalVO.getStrStepName(), ConstantsCommon.WORK_FLOW_STATUS_REJECT, adminApprovalVO.getStrUserNotification());
        		}
        		appMetaDataDomainObjectManager.update(appMetaData);
        		return completeStepVO;
        	}
        	
        	return null;
	}

        /**
         * createClientCopmany
         * 
         * @param adminApprovalVO
         * void
         */
        private void createClientCompanyAdmin(AdminApprovalVO adminApprovalVO, ClientCompanyVO clientCompanyVO) {
        	List<Service> services = findServicesToAdd(clientCompanyVO.getStrTrakheesId());
        	if(services != null){
        		getClientCompanyAdminDefaultServices(services);
        	}

			/************* Storing the Client **********************/
			Client clientRegistered = this.clientDomainObjectManager.findByTrakheesId(clientCompanyVO.getStrTrakheesId());
			Client client;
	
			if (clientRegistered != null)
				client = clientRegistered;
			else
				client = new Client();
	
			getMapper().map(clientCompanyVO, client);
	
			for (User user : client.getUsers()) {
				user.setClient(client);
				user.setUStatus(ConstantsCommon.APPROVE);
			}
			Set<ClientServiceRole> clientServiceRoles = new HashSet<ClientServiceRole>();
			List<UserServiceSecurity> userServiceSecurities = new ArrayList<UserServiceSecurity>();
			getServicesAndRoles(services, clientServiceRoles, userServiceSecurities, clientRegistered, client);
			if (clientRegistered == null) {
				if (clientServiceRoles.size() > 0)
					client.setClientServiceRoles(clientServiceRoles);
			}
	
			if (client != null)
				clientDomainObjectManager.update(client);
	
			/************* End Client Store **********************/
			
			/************* Storing User Service Security *********/
			for (UserServiceSecurity security : userServiceSecurities) {
				this.userServiceSecurityDomainObjectManager.add(security);
			}
			/*********** End Storing User Service Security *******/
     }
	/**
	 * findServicesToAdd
	 * @param strTrakheesId
	 * @return
	 */
	private List<Service> findServicesToAdd(String strTrakheesId) {
		List<SCClientCompanyRoleVO> companyRoleVOs = null;
		List<Service> services = new ArrayList<Service>();
		if (strTrakheesId != null) {
			companyRoleVOs = this.singleClientCompanyDomainObjectManager.findSCCompanyRoleByTrakheesId(strTrakheesId);
		}
		if(companyRoleVOs!=null)					
		{
			List<Long> list = new ArrayList<Long>();
			
			for (SCClientCompanyRoleVO scClientCompanyRoleVO : companyRoleVOs) {
				list.add(scClientCompanyRoleVO.getCrId());
			}
			
			if(list.size() > 0)
				services = this.serviceDomainObjectManager.findServiceByServiceRoleIds(list, ConstantsCommon.SERVICE_DEFAULT);
			
		}
		return services;
	}
     /**
	 * getClientCompanyAdminDefaultServices
	 * 
	 * @param services
	 * void
	 */
	private void getClientCompanyAdminDefaultServices(List<Service> services) {
	    	// Start - Adding the default services like User Default Service and Company Admin Service 
		Service userDefaultService = this.serviceDomainObjectManager.findUserDefaultService();

		if (userDefaultService != null)
			services.add(userDefaultService);
		
		Service companyAdminDefaultService = serviceDomainObjectManager.findServiceByServiceDefault(ConstantsCommon.COMPANY_ADMIN_DEFAULT_SERVICE);
		if(companyAdminDefaultService != null){
			services.add(companyAdminDefaultService);
		}
		// End - Adding the default services like User Default Service and Company Admin Service
	}

	/**
	 * getServicesAndRoles
         * @param userServiceSecurities 
         * @param clientServiceRoles 
         * @param services 
         * @param client 
         * @param clientRegistered 
	 * 
	 * @return
	 * List<UserServiceSecurity>
	 */
	private void getServicesAndRoles(List<Service> services, Set<ClientServiceRole> clientServiceRoles, List<UserServiceSecurity> userServiceSecurities, Client clientRegistered, Client client) {

		if (services != null) {

			for (Service service : services) {

				for (ServiceRole serviceRole : service.getServiceRoles()) {
					// if the client is already registered we don't need
					// to set this
					if (clientRegistered == null) {
						ClientServiceRole clientServiceRole = new ClientServiceRole();
						clientServiceRole.setServiceRole(serviceRole);
						clientServiceRole.setClient(client);
						clientServiceRoles.add(clientServiceRole);
					}

					// For User Service Security
					UserServiceSecurity userServiceSecurity = new UserServiceSecurity();
					userServiceSecurity.setServiceRole(serviceRole);
					userServiceSecurity.setUsSecurityUsername(client.getUsers().iterator().next().getUUsername());
					//userServiceSecurity.setUsSecurityUserType(client.getCType());
					userServiceSecurity.setUsSecurityUserType(client.getUsers().iterator().next().getUType());
					userServiceSecurities.add(userServiceSecurity);
				}
			}

		}
		
	}

	/**
         * getCompleteStepVO
         * To get the CompleteStepVO with the values populated
         * @param adminApprovalVO
         * @param clientCompanyVO 
         * @param appMetaData
         * @return
         * ClientRoleCompleteStepVO
         */
    private ClientAdminCompleteStepVO getCompleteStepVO(AdminApprovalVO adminApprovalVO, ClientCompanyVO clientCompanyVO, AppMetaData appMetaData) {
    	ClientAdminCompleteStepVO completeStepVO = new ClientAdminCompleteStepVO();
    	completeStepVO.setgApplicationID(adminApprovalVO.getStrApplicationId());
		completeStepVO.setgClientReference(appMetaData.getClientRefNo());
		completeStepVO.setStrStepName(adminApprovalVO.getStrStepName());
		completeStepVO.setStrUserName(adminApprovalVO.getStrUserName());
		completeStepVO.setgName(clientCompanyVO.getUserVOs().get(0).getStrUserName());
		completeStepVO.setgEmail(clientCompanyVO.getUserVOs().get(0).getStrEmail());
		completeStepVO.setnIncidentNumber(Integer.parseInt(adminApprovalVO.getStrIncidentNo()));
    	completeStepVO.setgApplicationID(adminApprovalVO.getStrApplicationId());
    	completeStepVO.setgClientReference(appMetaData.getClientRefNo());
    	completeStepVO.setStrStepName(adminApprovalVO.getStrStepName());
    	completeStepVO.setStrUserName(adminApprovalVO.getStrUserName());
    	return completeStepVO;
    }
    
	@Transactional
	public void undoReviewRegisterClientCompany(AdminApprovalVO adminApprovalVO, ClientCompanyVO clientCompanyVO) {
		AppMetaData appMetaData = appMetaDataDomainObjectManager.findByPrimaryKeyProcessAndStepCode(adminApprovalVO
				.getStrApplicationId().toString(), adminApprovalVO.getProcessCode().getProcessCode(), adminApprovalVO.getStrStepName());

		if (appMetaData != null) {
			adminApprovalVO.setStrApplnStatus(ConstantsCommon.IN_PROGRESS);
			getMapper().map(adminApprovalVO, appMetaData);
			
			this.appMetaDataDomainObjectManager.undoUpdateAppMetaDataWithActionTaken(appMetaData, adminApprovalVO.getStrStepName());

			if(clientCompanyAdminWF.getFinDeptStepName().equalsIgnoreCase(adminApprovalVO.getStrStepName()) && adminApprovalVO.getStrActionType().equals(ConstantsCommon.APPROVE)){
			    	// If Approved by Finance department, send the approval message to the Ultimus alone and update the application request in the DB as approved
			    	undoCreateClientCompanyAdmin(adminApprovalVO, clientCompanyVO);
			}
			if(clientCompanyAdminWF.getFinDeptStepName().equalsIgnoreCase(adminApprovalVO.getStrStepName()) || adminApprovalVO.getStrActionType().equals(ConstantsCommon.REJECT)){
		    	UserReq userReq = this.userReqDomainObjectManager.findByApplicationId(adminApprovalVO.getStrApplicationId());
				userReq.setUReqStatus(ConstantsCommon.IN_PROGRESS);
				this.userReqDomainObjectManager.update(userReq);
				
			}
			appMetaData.setWfStatus(ConstantsCommon.IN_PROGRESS);
			appMetaData.setWfStatusEn(adminApprovalVO.getStrInProgressStatusEn());
			appMetaData.setWfStatusAr(adminApprovalVO.getStrInProgressStatusAr());
			appMetaDataDomainObjectManager.update(appMetaData);
		}
	}

	private void undoCreateClientCompanyAdmin(AdminApprovalVO adminApprovalVO, ClientCompanyVO clientCompanyVO) {
    	List<Service> services = findServicesToAdd(clientCompanyVO.getStrTrakheesId());
    	if(services != null){
    		getClientCompanyAdminDefaultServices(services);
    		Client client = this.clientDomainObjectManager.findByTrakheesId(clientCompanyVO.getStrTrakheesId());
    		if(client != null){
			undoAddServicesAndRoles(services, client);
    			Set<User> users = client.getUsers();
    			for(User user : users){
			    userDomainObjectManager.delete(user);
			}
			client.getUsers().clear();

    		}
    	}
	}
	private void undoAddServicesAndRoles(List<Service> services, Client client) {

		if (services != null) {
			
			Set<UserServiceSecurity> ussSet = new HashSet<UserServiceSecurity>();

			for (Service service : services) {

				for (ServiceRole serviceRole : service.getServiceRoles()) {
					// if the client is already registered we don't need
					// to set this
					ClientServiceRole clientServiceRole = new ClientServiceRole();
					clientServiceRole.setServiceRole(serviceRole);
					clientServiceRole.setClient(client);
					List<ClientServiceRole> clientServiceRoles = this.clientDomainObjectManager.findClientServiceRole(clientServiceRole);
					if(clientServiceRoles != null){
						for(ClientServiceRole csr : clientServiceRoles){
							this.clientDomainObjectManager.delete(csr);
						}
					}

					// For User Service Security
					UserServiceSecurity userServiceSecurity = new UserServiceSecurity();
					userServiceSecurity.setServiceRole(serviceRole);
					userServiceSecurity.setUsSecurityUsername(client.getUsers().iterator().next().getUUsername());
					//userServiceSecurity.setUsSecurityUserType(client.getCType());
					userServiceSecurity.setUsSecurityUserType(client.getUsers().iterator().next().getUType());
					List<UserServiceSecurity> ussList = this.userServiceSecurityDomainObjectManager.findUserServiceSecurity(userServiceSecurity);
					ussSet.addAll(ussList);
				}
			}
			for(UserServiceSecurity uss : ussSet){
				this.userServiceSecurityDomainObjectManager.delete(uss);
			}
		}
		
	}

}
